﻿(function ($) {
    function wrapTree(target) {
        var tree = $(target);
        tree.addClass('tree');

        wrapNode(tree, 0);

        function wrapNode(ul, depth) {
            $('>li', ul).each(function () {
                var node = $('<div class="tree-node"></div>').prependTo($(this));
                var text = $('>span', this).addClass('tree-title').appendTo(node).text();
                $.data(node[0], 'tree-node', {
                    text: text
                });
                var subTree = $('>ul', this);
                if (subTree.length) {
                    $('<span class="tree-folder tree-folder-open"></span>').prependTo(node);
                    $('<span class="tree-hit tree-expanded"></span>').prependTo(node);
                    wrapNode(subTree, depth + 1);
                } else {
                    $('<span class="tree-file"></span>').prependTo(node);
                    $('<span class="tree-indent"></span>').prependTo(node);
                }
                for (var i = 0; i < depth; i++) {
                    $('<span class="tree-indent"></span>').prependTo(node);
                }
            });
        }
        return tree;
    }

    function expandNode(target, node, status) {
        var opts = $.data(target, 'tree').options;

        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return;    // is a leaf node

        if (hit.hasClass('tree-collapsed')) {
            hit.removeClass('tree-collapsed tree-collapsed-hover').addClass('tree-expanded');
            hit.next().addClass('tree-folder-open');
            var ul = $(node).next();
            if (ul.length) {
                if (opts.animate) {
                    ul.slideDown();
                } else {
                    ul.css('display', 'block');
                }
            } else {
                var nodeData = $.data($(node)[0], 'tree-node');
                var subul = $('<ul></ul>').insertAfter(node);
                request(target, subul, nodeData, status);        // request children nodes data
            }
        }
    }

    function uncheck(target, node) {
        var cbNode = $(node).find('.tree-checkbox');
        if ((cbNode.hasClass('tree-checkbox1') || cbNode.hasClass('tree-checkbox2')) && !cbNode.hasClass('tree-checkbox3')) {
            $(node).find('.tree-checkbox').removeClass('tree-checkbox1 tree-checkbox2').addClass('tree-checkbox0');
        }
    }

    function uncheckAllCheck(target, exceptNode) {
        /// <summary>uncheck所有Checkbox，除掉指定的节点
        ///   <para></para>
        /// </summary>
        /// <param name="target" type="object"></param> 
        /// <param name="exceptNode" type="object">要排除的节点</param>
        $(target).find('.tree-checkbox1').each(function () {
            var node = $(this).parent();
            if (node[0] != exceptNode)
                $(this).removeClass('tree-checkbox1').addClass('tree-checkbox0');
        });
    }

    function uncheckAllRadio(target, exceptNode) {
        /// <summary>uncheck所有Radio，除掉指定的节点
        ///   <para></para>
        /// </summary>
        /// <param name="target" type="object"></param> 
        /// <param name="exceptNode" type="object">要排除的节点</param>
        $(target).find('.tree-radio1').each(function () {
            var node = $(this).parent();
            if (node[0] != exceptNode)
                $(this).removeClass('tree-radio1').addClass('tree-radio0');
        });
    }

    function check(target, node) {
        var cbNode = $(node).find('.tree-checkbox');
        if (cbNode.hasClass('tree-checkbox0') && !cbNode.hasClass('tree-checkbox3')) {
            $(node).find('.tree-checkbox0').removeClass('tree-checkbox0').addClass('tree-checkbox1');
            addCNodes(target, $(node));
        }
        else if (cbNode.hasClass('tree-radio0')) {
            $(node).find('.tree-radio0').removeClass('tree-radio0').addClass('tree-radio1');
        }
    }

    function checkByIDList(target, idList) {
        var roots = $('.tree').tree('getRoots');
        if (roots != null && roots.length > 0) {
            for (var i = 0; i < roots.length; i++) {
                checkNodeByIDList(target, roots[i], idList);
            }
        }
    }

    function checkNodeByIDList(target, node, idList) {
        if (idList.indexOf(node.id) >= 0) {
            check(target, node.target);
        }
        var children = $('.tree').tree('getChildren', node.target);
        if (children != null && children.length > 0) {
            for (var i = 0; i < children.length; i++) {
                checkNodeByIDList(target, children[i], idList);
            }
        }
    }

    function checkEnable(target, param, enable) {
        var cbNode = $(param).find('.tree-checkbox');
        if (enable) {
            cbNode.removeClass('tree-checkbox3');
        }
        else {
            cbNode.addClass('tree-checkbox3');
        }
    }

    function checkDisableAll(target, exceptNode) {
        /// <summary>禁用所有的checkbox，除掉指定的节点
        ///   <para></para>
        /// </summary>
        /// <param name="target" type="object"></param> 
        /// <param name="exceptNode" type="object">要排除的节点</param>
        $(target).find('.tree-checkbox').each(function () {
            if (exceptNode != null && exceptNode != undefined && isChild(target, exceptNode, $(this).parent()[0])) {
                if ($(this).hasClass('tree-checkbox3')) {
                    $(this).removeClass('tree-checkbox3').addClass('tree-checkbox0');
                }
            }
            else {
                if ($(this).hasClass('tree-checkbox0')) {
                    $(this).removeClass('tree-checkbox0').addClass('tree-checkbox3');
                } else if ($(this).hasClass('tree-checkbox1')) {
                    $(this).removeClass('tree-checkbox1').addClass('tree-checkbox3');
                } else if ($(this).hasClass('tree-checkbox2')) {
                    $(this).removeClass('tree-checkbox2').addClass('tree-checkbox3');
                }
            }
        });
    }

    function isChild(target, node, child) {
        /// <summary>判断两个节点是否有父子关系
        ///   <para></para>
        /// </summary>
        /// <param name="target" type="object"></param> 
        /// <param name="node" type="object">node 父辈</param> 
        /// <param name="child" type="object">child 儿子辈</param>
        var parentNode = child;
        var ret = (node == parentNode);
        while (parentNode != null && ret == false) {
            var curNode = getParentNode(target, parentNode);
            if (curNode != null && curNode.id != undefined)
                parentNode = curNode.target;
            else
                parentNode = null;
            ret = (node == parentNode);
        }
        return ret;
    }

    function collapseNode(target, node) {
        var opts = $.data(target, 'tree').options;

        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return;    // is a leaf node

        if (hit.hasClass('tree-expanded')) {
            hit.removeClass('tree-expanded tree-expanded-hover').addClass('tree-collapsed');
            hit.next().removeClass('tree-folder-open');
            if (opts.animate) {
                $(node).next().slideUp();
            } else {
                $(node).next().css('display', 'none');
            }
        }
    }

    function toggleNode(target, node) {
        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return;    // is a leaf node

        if (hit.hasClass('tree-expanded')) {
            collapseNode(target, node);
        } else {
            expandNode(target, node);
        }
    }

    function bindTreeEvents(target) {
        var opts = $.data(target, 'tree').options;
        var tree = $.data(target, 'tree').tree;

        $('.tree-node', tree).unbind('.tree').bind('click.tree', function () {
            //当前选择状态
            $('.tree-node-selected', tree).removeClass('tree-node-selected');
            $(this).addClass('tree-node-selected');

            if (opts.onClick) {
                var target = this;      // the target HTML DIV element
                var data = $.data(this, 'tree-node');
                opts.onClick.call(this, {
                    id: data.id,
                    text: data.text,
                    attributes: data.attributes,
                    target: target
                });
            }
            return false;
        }).bind('mouseenter.tree', function () {
            $(this).addClass('tree-node-hover');
            return false;
        }).bind('mouseleave.tree', function () {
            $(this).removeClass('tree-node-hover');
            return false;
        }).bind('contextmenu.tree', function (e) {
            $('.tree-node-selected', tree).removeClass('tree-node-selected');
            $(this).addClass('tree-node-selected');
            if (opts.onContextMenu) {
                var target = this;    // the target HTML DIV element
                var data = $.data(this, 'tree-node');
                opts.onContextMenu.call(this, e, {
                    id: data.id,
                    text: data.text,
                    attributes: data.attributes
                });
            }
            return false;
        });

        $('.tree-hit', tree).unbind('.tree').bind('click.tree', function () {
            var node = $(this).parent();
            toggleNode(target, node);
            return false;
        }).bind('mouseenter.tree', function () {
            if ($(this).hasClass('tree-expanded')) {
                $(this).addClass('tree-expanded-hover');
            } else {
                $(this).addClass('tree-collapsed-hover');
            }
        }).bind('mouseleave.tree', function () {
            if ($(this).hasClass('tree-expanded')) {
                $(this).removeClass('tree-expanded-hover');
            } else {
                $(this).removeClass('tree-collapsed-hover');
            }
        });

        $('.tree-checkbox', tree).unbind('.tree').bind('click.tree', function () {
            var checked = false;
            if ($(this).hasClass('tree-radio0') || $(this).hasClass('tree-radio1')) {
                checked = setSiblingsRaido($(this).parent());
            } else {
                if ($(this).hasClass('tree-checkbox3')) {
                    return false;
                }
                if ($(this).hasClass('tree-checkbox0')) {
                    $(this).removeClass('tree-checkbox0').addClass('tree-checkbox1');
                    addCNodesByOpts(opts, $(this).parent());
                    checked = true;
                } else if ($(this).hasClass('tree-checkbox1')) {
                    $(this).removeClass('tree-checkbox1').addClass('tree-checkbox0');
                    removeCNodesByOpts(opts, $(this).parent());
                    checked = false;
                } else if ($(this).hasClass('tree-checkbox2')) {
                    $(this).removeClass('tree-checkbox2').addClass('tree-checkbox1');
                    addCNodesByOpts(opts, $(this).parent());
                    checked = false;
                }
                if (opts.cascadeCheck) {
                    var node = $(this).parent();
                    var childRadio = node.next().find('.tree-radio0').add('.tree-radio1');
                    if (childRadio.length > 0) {
                        var ck = node.find('.tree-checkbox');
                        return;
                    }
                    setParentCheckbox($(this).parent());

                    var target = $(this).parent('.tree-node')[0];
                    if (target != null) {
                        var data = $.data(target, 'tree-node');
                        if (data != null && (data.attributes == null || data.attributes.cascadeCheck)) {
                            setChildCheckbox($(this).parent());
                        }
                    }
                }
            }
            //this is useless
            if (opts.onCheck) {
                var target = $(this).parent('.tree-node')[0];      // the target HTML DIV element
                var data = $.data(target, 'tree-node');
                opts.onCheck.call(target, {
                    id: data.id,
                    text: data.text,
                    attributes: data.attributes,
                    target: target
                }, checked);
            }
            return false;
        });

        function setSiblingsRaido(node) {
            //1 将父选上
            //2 同侪只能选一个
            //3 可以取消自己
            var pnode = getParentNode(target, node[0]);
            $(pnode.target).find('.tree-checkbox0').add('.tree-checkbox1').removeClass('tree-checkbox0').addClass('tree-checkbox1');
            addCNodes(target, $(pnode.target));
            var isRadio1 = $(node.find('.tree-checkbox')[0]).hasClass('tree-radio1');
            if (isRadio1) {
                //alert(262);
                node.find('.tree-checkbox').removeClass('tree-radio1').addClass('tree-radio0');
                return false;
            } else {
                //alert(265);
                node.parent().parent().find('.tree-radio1').removeClass('tree-radio1').addClass('tree-radio0');
                node.find('.tree-checkbox').removeClass('tree-radio0').addClass('tree-radio1');
                return true;
            }
        }

        function setChildCheckbox(node) {
            var ck = node.find('.tree-checkbox');
            if (ck.hasClass('tree-radio0') || ck.hasClass('tree-radio1')) {
                return;
            }
            var childck = node.next().find('.tree-checkbox');

            childck.removeClass('tree-checkbox0 tree-checkbox1 tree-checkbox2')
            if (node.find('.tree-checkbox').hasClass('tree-checkbox1')) {
                childck.addClass('tree-checkbox1');
                addCNodes(target, $(childck));
            } else {
                childck.addClass('tree-checkbox0');
            }
        }

        function setParentCheckbox(node) {
            var pnode = getParentNode(target, node[0]);
            if (pnode) {
                var ck = $(pnode.target).find('.tree-checkbox');
                if (ck.hasClass('tree-radio0') || ck.hasClass('tree-radio1')) {
                    ck.removeClass('tree-radio1').addClass('tree-radio0');
                    //alert(285);
                    return;
                }
                if (pnode.attributes == null || pnode.attributes.cascadeCheck) {
                    ck.removeClass('tree-checkbox0 tree-checkbox1 tree-checkbox2');
                    if (isAllSelected(node, $(pnode.target))) {
                        ck.addClass('tree-checkbox1');
                        addCNodes(target, $(ck));
                    } else if (isAllNull(node, $(pnode.target))) {
                        ck.addClass('tree-checkbox0');
                    } else {
                        ck.addClass('tree-checkbox2');
                    }
                }
                setParentCheckbox($(pnode.target));
            }

            function isAllSelected(n, pn) {
                var ck = n.find('.tree-checkbox');
                if (ck.hasClass('tree-checkbox0') || ck.hasClass('tree-checkbox2')) return false;

                var b = true;
                pn.next().children("li").find('.tree-checkbox').each(function () {
                    if (!$(this).hasClass('tree-checkbox1')) {
                        b = false;
                        return b;
                    }
                });
                return b;
            }

            function isAllNull(n, pn) {
                var ck = n.find('.tree-checkbox');
                if (ck.hasClass('tree-checkbox1') || ck.hasClass('tree-checkbox2')) return false;

                var b = true;
                pn.next().children("li").find('.tree-checkbox').each(function () {
                    if (!$(this).hasClass('tree-checkbox0')) {
                        b = false;
                        return b;
                    }
                });
                return b;
            }
        }
    }

    function setCheckboxByChildrenStatus(opts, node) {
        var ck = node.find('.tree-checkbox');
        if (ck.hasClass('tree-radio0') || ck.hasClass('tree-radio1')) {
            ck.removeClass('tree-radio1').addClass('tree-radio0');
            return;
        }
        ck.removeClass('tree-checkbox0 tree-checkbox1 tree-checkbox2');
        if (ChildrenIsAllSelected(node)) {
            ck.addClass('tree-checkbox1');
            addCNodesByOpts(opts, node);
        } else if (ChildrenIsAllNull(node)) {
            ck.addClass('tree-checkbox0');
            removeCNodesByOpts(opts, node);
        } else {
            ck.addClass('tree-checkbox2');
        }

        function ChildrenIsAllSelected(node) {
            var b = true;
            node.next().children("li").find('.tree-checkbox').each(function () {
                if (!$(this).hasClass('tree-checkbox1')) {
                    b = false;
                    return b;
                }
            });
            return b;
        }
        function ChildrenIsAllNull(node) {
            var b = true;
            node.next().children("li").find('.tree-checkbox').each(function () {
                if (!$(this).hasClass('tree-checkbox0')) {
                    b = false;
                    return b;
                }
            });
            return b;
        }
    }

    function loadData(target, ul, data) {
        // clear the tree when loading to the root
        if (target == ul) {
            $(target).empty();
        }
        else {
            $(ul).empty();
        }

        var opts = $.data(target, 'tree').options;
        function appendNodes(ul, children, depth) {
            for (var i = 0; i < children.length; i++) {
                var li = $('<li></li>').appendTo(ul);
                var item = children[i];

                // the node state has only 'open' or 'closed' attribute
                if (item.state != 'open' && item.state != 'closed') {
                    item.state = 'open';
                }

                var node = $('<div class="tree-node"></div>').appendTo(li);
                node.attr('node-id', item.id);

                // store node attributes
                $.data(node[0], 'tree-node', {
                    id: item.id,
                    text: item.text,
                    attributes: item.attributes
                });
                var curNode = $('<span class="tree-title"></span>').html(item.text).appendTo(node);
                if (item.attributes != null && item.attributes.color != "" && item.attributes.color != null) {
                    curNode.css("background", item.attributes.color);
                }
                if (item.hasRadio == true) {
                    if (item.attributes != null && (item.attributes.initRadio || isInRadioNodes(target, item))) {
                        $('<span class="tree-checkbox tree-radio1"></span>').prependTo(node);
                    } else {
                        $('<span class="tree-checkbox tree-radio0"></span>').prependTo(node);
                    }
                }
                else if (opts.checkbox && item.hasCheckbox != "false") {
                    if (item.attributes != null && (item.attributes.initCheck || isInCheckNodes(target, item))) {
                        $('<span class="tree-checkbox tree-checkbox1"></span>').prependTo(node);
                    } else {
                        $('<span class="tree-checkbox tree-checkbox0"></span>').prependTo(node);
                    }
                }
                if (item.children && item.children.length > 0) {
                    var subul = $('<ul></ul>').appendTo(li);

                    if (item.state == 'open') {
                        curNode = $('<span class="tree-folder tree-folder-open"></span>').addClass(item.iconCls).prependTo(node);
                        $('<span class="tree-hit tree-expanded"></span>').prependTo(node);
                    } else {
                        curNode = $('<span class="tree-folder"></span>').addClass(item.iconCls).prependTo(node);
                        $('<span class="tree-hit tree-collapsed"></span>').prependTo(node);
                        subul.css('display', 'none');
                    }
                    appendNodes(subul, item.children, depth + 1);
                    if (opts.cascadeCheck && (item.attributes == null || item.attributes.cascadeCheck)) {
                        setCheckboxByChildrenStatus(opts, node);
                    }
                } else {
                    if (item.state == 'closed') {
                        curNode = $('<span class="tree-folder"></span>').addClass(item.iconCls).prependTo(node);
                        $('<span class="tree-hit tree-collapsed"></span>').prependTo(node)
                    } else {
                        //                                              $('<input type="checkbox" style="vertical-align:bottom;margin:0;height:18px;">').prependTo(node);
                        curNode = $('<span class="tree-file"></span>').addClass(item.iconCls).prependTo(node);
                        $('<span class="tree-indent"></span>').prependTo(node);
                    }
                }
                if (item.attributes != null && item.attributes.image != "" && item.attributes.image != null)
                    curNode.css("background-image", "url(\"" + item.attributes.image + "\")");
                for (var j = 0; j < depth; j++) {
                    $('<span class="tree-indent"></span>').prependTo(node);
                }
            }
        }

        var depth = $(ul).prev().find('>span.tree-indent,>span.tree-hit').length;
        appendNodes(ul, data, depth);

    }

    /**
    * request remote data and then load nodes in the <ul> tag.
    */
    function request(target, ul, param, status) {
        var opts = $.data(target, 'tree').options;
        if (!opts.url) return;

        param = param || {};

        var folder = $(ul).prev().find('>span.tree-folder');
        folder.addClass('tree-loading');
        $.ajax({
            type: 'post',
            url: opts.url,
            data: param,
            dataType: 'json',
            success: function (data) {
                folder.removeClass('tree-loading');
                loadData(target, ul, data);
                bindTreeEvents(target);
                if (opts.onLoadSuccess) {
                    opts.onLoadSuccess.apply(this, arguments);
                }
                if (status != undefined)
                    setChildrenStatus(target, ul[0], status);
            },
            error: function () {
                folder.removeClass('tree-loading');
                if (opts.onLoadError) {
                    opts.onLoadError.apply(this, arguments);
                }
            }
        });
    }

    /**
    * get the parent node
    * param: DOM object, from which to search it's parent node
    */
    function getParentNode(target, param) {
        var node = $(param).parent().parent().prev();
        if (node.length) {
            return $.extend({}, $.data(node[0], 'tree-node'), {
                target: node[0],
                checked: node.find('.tree-checkbox').hasClass('tree-checkbox1'),
                radiochecked: node.find('.tree-checkbox').hasClass('tree-radio1')
            });
        } else {
            return null;
        }
    }

    function getChildren(target, param) {
        var nodes = [];
        if (param != null) {
            var node = $(param).next();
            if (node.length) {
                var children = node.children("li");
                if (children.length) {
                    children.each(function () {
                        var curNode = $(this).children("div");
                        nodes.push($.extend({}, $.data(curNode[0], 'tree-node'), {
                            target: curNode[0],
                            checked: curNode.find('.tree-checkbox').hasClass('tree-checkbox1'),
                            radiochecked: curNode.find('.tree-checkbox').hasClass('tree-radio1')
                        }));
                    });
                }
            }
        }
        return nodes;
    }

    function getRoots(target) {
        var nodes = [];
        if (target.children != null && target.children.length > 0) {
            for (var i = 0; i < target.children.length; i++) {
                var curNode = $(target.children[i]).children("div");
                nodes.push($.extend({}, $.data(curNode[0], 'tree-node'), {
                    target: curNode[0],
                    checked: curNode.find('.tree-checkbox').hasClass('tree-checkbox1'),
                    radiochecked: curNode.find('.tree-checkbox').hasClass('tree-radio1')
                }));
            }
        }
        return nodes;
    }

    /// <summary>得到树展开状态
    ///   <para></para>
    /// </summary>
    /// <param name="target" type="object">树</param> 
    function getStatus(target) {
        var nodes = [];
        nodes = getChildrenStatus(target);
        return nodes;
    }

    /// <summary>当前节点是否展开
    ///   <para></para>
    /// </summary>        
    /// <param name="node" type="object">节点</param> 
    function IsExpanded(node) {
        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return false;
        return hit.hasClass('tree-expanded');
    }

    /// <summary>得到子节点是否展开
    ///   <para></para>
    /// </summary>
    /// <param name="parentNode" type="object">指定的父节点</param> 
    function getChildrenStatus(parentNode) {
        var childrenNode = [];
        if (parentNode != null && parentNode.children != null && parentNode.children.length > 0) {
            for (var i = 0; i < parentNode.children.length; i++) {
                var nt = $(parentNode.children[i]).children('div')[0];
                if (IsExpanded(nt)) {
                    var node = $.data(nt, 'tree-node');
                    childrenNode.push({
                        id: node.id,
                        isSelected: $(nt).hasClass('tree-node-selected'),
                        isChecked: $(nt).hasClass('tree-checkbox1'),
                        isRadioed: $(nt).hasClass('tree-radio1'),
                        children: getChildrenStatus($(nt).next()[0])
                    });
                }
            }
        }
        return childrenNode;
    }

    function setStatus(target, status) {
        /// <summary>设置树的展开状态
        ///   <para></para>
        /// </summary>
        /// <param name="target" type="object">树</param> 
        /// <param name="status" type="array">状态树</param> 
        setChildrenStatus(target, target, status);
    }

    function setChildrenStatus(target, parentNode, status) {
        /// <summary>设置儿子节点展开状态
        ///   <para></para>
        /// </summary>
        /// <param name="target" type="object">树</param> 
        /// <param name="parentNode" type="object">指定的父节点</param> 
        /// <param name="status" type="array">儿子状态树</param> 
        for (var i = 0; i < status.length; i++) {
            if (parentNode.children != null && parentNode.children.length > 0) {
                for (var j = 0; j < parentNode.children.length; j++) {
                    var nt = $(parentNode.children[j]).children('div')[0];
                    var node = $.data(nt, 'tree-node');
                    if (node.id == status[i].id) {
                        expandNode(target, nt, status[i].children);
                    }
                }
            }
        }
    }

    function getParentNode2(target, param) {
        var node = $(param).parent().parent().prev();
        if (node.length) {
            return $.extend({}, $.data(node[0], 'tree-node'), {
                target: node[0],
                checked: node.find('.tree-checkbox').hasClass('tree-checkbox1'),
                radiochecked: node.find('.tree-checkbox').hasClass('tree-radio1')
            });
        } else {
            return null;
        }
    }

    function getCheckedNode(target, param, allChildren) {
        var nodes = [];
        if (param != null) {
            var node = $(param).next();
            if (node.length) {
                if (allChildren != null && allChildren) {
                    $(node).find('.tree-checkbox1').each(function () {
                        var node = $(this).parent();
                        nodes.push($.extend({}, $.data(node[0], 'tree-node'), {
                            target: node[0],
                            checked: node.find('.tree-checkbox').hasClass('tree-checkbox1')
                        }));
                    });
                }
                else {
                    var children = node.children("li");
                    if (children.length) {
                        children.each(function () {
                            var curNode = $(this).children("div");
                            if (curNode.find('.tree-checkbox').hasClass('tree-checkbox1')) {
                                nodes.push($.extend({}, $.data(curNode[0], 'tree-node'), {
                                    target: curNode[0],
                                    checked: curNode.find('.tree-checkbox').hasClass('tree-checkbox1')
                                }));
                            }
                        });
                    }
                }
            }
        }
        else {
            $(target).find('.tree-checkbox1').each(function () {
                var node = $(this).parent();
                nodes.push($.extend({}, $.data(node[0], 'tree-node'), {
                    target: node[0],
                    checked: node.find('.tree-checkbox').hasClass('tree-checkbox1')
                }));
            });
        }
        return nodes;
    }

    function getRadioedNode(target, param, allChildren) {
        var nodes = [];
        if (param != null) {
            var node = $(param).next();
            if (node.length) {
                if (allChildren != null && allChildren) {
                    $(node).find('.tree-radio1').each(function () {
                        var node = $(this).parent();
                        nodes.push($.extend({}, $.data(node[0], 'tree-node'), {
                            target: node[0],
                            checked: true
                        }));
                    });
                }
                else {
                    var children = node.children("li");
                    if (children.length) {
                        children.each(function () {
                            var curNode = $(this).children("div");
                            if (curNode.hasClass('.tree-radio1')) {
                                nodes.push($.extend({}, $.data(curNode[0], 'tree-node'), {
                                    target: curNode[0],
                                    checked: true
                                }));
                            }
                        });
                    }
                }
            }
        }
        else {
            $(target).find('.tree-radio1').each(function () {
                var node = $(this).parent();
                nodes.push($.extend({}, $.data(node[0], 'tree-node'), {
                    target: node[0],
                    checked: true
                }));
            });
        }
        return nodes;
    }

    /**
    * Get the selected node data which contains following properties: id,text,attributes,target
    */
    function getSelectedNode(target) {
        var node = $(target).find('div.tree-node-selected');
        if (node.length) {
            return $.extend({}, $.data(node[0], 'tree-node'), {
                target: node[0],
                checked: node.find('.tree-checkbox').hasClass('tree-checkbox1'),
                radiochecked: node.find('.tree-checkbox').hasClass('tree-radio1')
            });
        } else {
            return null;
        }
    }

    function getByID(target, id) {
        var retNode = null;
        $(target).find('li').each(function () {
            var curNode = $(this).children("div");
            var node = $.data(curNode[0], 'tree-node');
            if (node.id == id) {
                retNode = $.extend({}, node, { target: curNode[0] });
                return retNode;
            }
        });
        return retNode;
    }

    /**
    * Append nodes to tree.
    * The param parameter has two properties:
    * 1 parent: DOM object, the parent node to append to.
    * 2 data: array, the nodes data.
    */
    function appendNodes(target, param) {
        var node = $(param.parent);
        var ul = node.next();
        if (ul.length == 0) {
            ul = $('<ul></ul>').insertAfter(node);
        }

        // ensure the node is a folder node
        if (param.data && param.data.length) {
            var nodeIcon = node.find('span.tree-file');
            if (nodeIcon.length) {
                nodeIcon.removeClass('tree-file').addClass('tree-folder');
                var hit = $('<span class="tree-hit tree-expanded"></span>').insertBefore(nodeIcon);
                if (hit.prev().length) {
                    hit.prev().remove();
                }
            }
        }

        loadData(target, ul, param.data);
        bindTreeEvents(target);
    }

    /**
    * Remove node from tree.
    * param: DOM object, indicate the node to be removed.
    */
    function removeNode(target, param) {
        var node = $(param);
        var li = node.parent();
        var ul = li.parent();
        li.remove();
        if (ul.find('li').length == 0) {
            var node = ul.prev();
            node.find('.tree-folder').removeClass('tree-folder').addClass('tree-file');
            node.find('.tree-hit').remove();
            $('<span class="tree-indent"></span>').prependTo(node);
            if (ul[0] != target) {
                ul.remove();
            }
        }
    }

    /**
    * select the specified node.
    * param: DOM object, indicate the node to be selected.
    */
    function selectNode(target, param) {
        $('div.tree-node-selected', target).removeClass('tree-node-selected');
        $(param).addClass('tree-node-selected');
    }

    /**
    * Check if the specified node is leaf.
    * param: DOM object, indicate the node to be checked.
    */
    function isLeaf(target, param) {
        var node = $(param);
        var hit = $('>span.tree-hit', node);
        return hit.length == 0;
    }

    function reload(target, param) {
        var ul = target;
        if (param != undefined && param != null) {
            var nodeData = $.data(param, 'tree-node');
            ul = $(param).next();
            if (ul.length) {
                ul.empty();
            }
            else {
                ul = $('<ul></ul>').insertAfter(param);
            }
        }
        request(target, ul, nodeData);
    }

    function getCRNodesByNode(target) {
        var rnodes = getRadioedNode(target);
        var cnodes = getCheckedNode(target);
        return { rnodes: rnodes, cnodes: cnodes };
    }

    function addCNodes(target, node) {
        var opts = $.data(target, 'tree').options;
        addCNodesByOpts(opts, node);
    }

    function addCNodesByOpts(opts, node) {
        if (opts.rcNodes.cnodes == null)
            opts.rcNodes.cnodes = [];
        if (node.length) {
            opts.rcNodes.cnodes.push($.data(node[0], 'tree-node'));
        }
    }

    function removeCNodes(target, node) {
        var opts = $.data(target, 'tree').options;
        removeCNodesByOpts(opts, node);
    }

    function removeCNodesByOpts(opts, node) {
        if (opts.rcNodes.cnodes != null) {
            var id = $.data(node[0], 'tree-node').id;
            for (var i = 0; i < opts.rcNodes.cnodes.length; i++) {
                if (opts.rcNodes.cnodes[i].id == id) {
                    opts.rcNodes.cnodes.splice(i, 1);
                    break;
                }
            }
        }
    }

    /// <summary>得到选中的checkbox和radiobox列表
    ///   <para></para>
    /// </summary>
    /// <param name="target" type="object">树</param>
    function getCRNodes(target) {
        var opts = $.data(target, 'tree').options;
        return opts.rcNodes;
    }

    /// <summary>设置选中的checkbox和radiobox列表
    ///   <para></para>
    /// </summary>
    /// <param name="target" type="object">树</param> 
    /// <param name="rcNodes" type="object">checkbox和radiobox列表</param>    
    function setCRNodes(target, rcNodes) {
        var opts = $.data(target, 'tree').options;
        opts.rcNodes = rcNodes;
    }

    /// <summary>判断指定节点是否在选中radiobox列表里
    ///   <para></para>
    /// </summary>
    /// <param name="target" type="object">树</param> 
    /// <param name="item" type="object">要判断的节点</param>  
    function isInRadioNodes(target, item) {
        var opts = $.data(target, 'tree').options;
        if (opts.rcNodes != null && opts.rcNodes.rnodes != null) {
            for (var i = 0; i < opts.rcNodes.rnodes.length; i++) {
                if (opts.rcNodes.rnode[i].id == item.id)
                    return true;
            }
        }
        return false;
    }

    /// <summary>判断指定节点是否在选中checkbox列表里
    ///   <para></para>
    /// </summary>
    /// <param name="target" type="object">树</param> 
    /// <param name="item" type="object">要判断的节点</param>  
    function isInCheckNodes(target, item) {
        var opts = $.data(target, 'tree').options;
        if (opts.rcNodes != null && opts.rcNodes.cnodes != null) {
            for (var i = 0; i < opts.rcNodes.cnodes.length; i++) {
                if (opts.rcNodes.cnodes[i].id == item.id)
                    return true;
            }
        }
        return false;
    }

    $.fn.tree = function (options, param, allChildren) {
        if (typeof options == 'string') {
            switch (options) {
                case 'options':
                    return $.data(this[0], 'tree').options;
                case 'reload':
                    return this.each(function () {
                        reload(this, param);
                    });
                case 'getParent':
                    return getParentNode(this[0], param);
                case 'getParent2':
                    return getParentNode2(this[0], param);
                case 'getChildren':
                    return getChildren(this[0], param);
                case 'getRoots':
                    return getRoots(this[0]);
                case 'getStatus':
                    return getStatus(this[0]);
                case 'setStatus':
                    return setStatus(this[0], param);
                case 'getChecked':
                    return getCheckedNode(this[0], param, allChildren);
                case 'getRadioed':
                    return getRadioedNode(this[0], param, allChildren);
                case 'getCRNodesByNode':
                    return getCRNodesByNode(this[0]);
                case 'getCRNodes':
                    return getCRNodes(this[0]);
                case 'setCRNodes':
                    return setCRNodes(this[0], param);
                case 'getSelected':
                    return getSelectedNode(this[0]);
                case 'getByID':
                    return getByID(this, param);
                case 'isLeaf':
                    return isLeaf(this[0], param);
                case 'select':
                    return this.each(function () {
                        selectNode(this, param);
                    });
                case 'collapse':
                    return this.each(function () {
                        collapseNode(this, $(param));
                    });
                case 'expand':
                    return this.each(function () {
                        expandNode(this, $(param));
                    });
                case 'append':
                    return this.each(function () {
                        appendNodes(this, param);
                    });
                case 'uncheck':
                    return this.each(function () {
                        uncheck(this, param);
                    });
                case 'uncheckAllCheck':
                    return this.each(function () {
                        uncheckAllCheck(this, param);
                    });
                case 'uncheckAllRadio':
                    return this.each(function () {
                        uncheckAllRadio(this, param);
                    });
                case 'check':
                    return this.each(function () {
                        check(this, param);
                    });
                case 'checkByIDList':
                    return this.each(function () {
                        checkByIDList(this, param);
                    });
                case 'checkEnable':
                    return this.each(function () {
                        checkEnable(this, param, allChildren);
                    });
                case 'checkDisableAll':
                    return this.each(function () {
                        checkDisableAll(this, param);
                    });
                case 'toggle':
                    return this.each(function () {
                        toggleNode(this, $(param));
                    });
                case 'remove':
                    return this.each(function () {
                        removeNode(this, param);
                    });
            }
        }

        var options = options || {};
        return this.each(function () {
            var state = $.data(this, 'tree');
            var opts;
            if (state) {
                opts = $.extend(state.options, options);
                state.options = opts;
            } else {
                opts = $.extend({}, $.fn.tree.defaults, {
                    url: $(this).attr('url'),
                    animate: ($(this).attr('animate') ? $(this).attr('animate') == 'true' : undefined)
                }, options);
                $.data(this, 'tree', {
                    options: opts,
                    tree: wrapTree(this)
                });
            }

            if (opts.url) {
                request(this, this);
            }
            bindTreeEvents(this);
        });
    };

    $.fn.tree.defaults = {
        url: null,
        animate: false,
        checkbox: false,
        cascadeCheck: true,
        rcNodes: { rnodes: null, cnodes: null },
        onLoadSuccess: function () { },
        onLoadError: function () { },
        onClick: function (node) { },
        onDblClick: function (node) { },
        onCheck: function (node, checked) { },
        onContextMenu: function (e, node) { }
    };
})(jQuery);